package com.unitd.modules.gen.util;

import com.google.common.collect.Lists;
import com.unitd.frame.comm.mapper.JaxbMapper;
import com.unitd.frame.comm.utils.*;
import com.unitd.frame.comm.utils.template.FreeMarkerUtils;
import com.unitd.frame.spring.prop.MyPropertyPlaceholderConfigurer;
import com.unitd.modules.gen.entity.*;
import com.unitd.modules.sys.entity.SysDict;
import com.unitd.modules.sys.entity.SysOrg;
import com.unitd.modules.sys.entity.SysUser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.Resource;

import java.io.*;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @desc 代码生成工具类
 * @filename GenUtils.java
 * @copyright www.unitd.com
 * @author Hudan
 * @version 1.0
 * @date 2017/4/27
 */
public class GenUtils {

	private static Logger logger = LoggerFactory.getLogger(GenUtils.class);

	/**
	 * @desc 初始化列属性字段
	 * @param genTable 生成表对象
	 */
	public static void initColumnField(GenTable genTable) {
		for (GenTableColumn column : genTable.getColumnList()) {

			// 如果不是新增列，则跳过
			if(!StringUtils.isZero(column.getId())) {
				continue;
			}

			// 设置字段说明
			if (StringUtils.isBlank(column.getComments())) {
				column.setComments(column.getColumnName());
			}

			// 设置java字段名(驼峰命名)
			column.setJavaField(StringUtils.toCamelCase(column.getColumnName()));
			column.setBaseJavaField(column.getJavaField());

			// 设置java类型; 根据列的数据库的数据类型,转换成对应的Java类型
			String javaType = getJavaType(column.getJdbcType());
			column.setJavaType(javaType);
			column.setBaseJavaType(javaType);

			// 是否是主键
			column.setIsPk(genTable.getPkList().contains(column.getColumnName().toLowerCase()) ? "1" : "0");
			if("1".equals(column.getIsPk())) {
				genTable.setPk(column); 												// 适用于仪表只有一个主键列
			}

			// 插入字段
			column.setIsInsert("1");

			// 编辑字段
			if (!StringUtils.equalsIgnoreCase(column.getColumnName(), "id")
					&& !StringUtils.equalsIgnoreCase(column.getColumnName(), "create_by")
					&& !StringUtils.equalsIgnoreCase(column.getColumnName(), "create_date")
					&& !StringUtils.equalsIgnoreCase(column.getColumnName(), "del_flag")) {
				column.setIsEdit("1");
			}

			// 列表字段
			if (StringUtils.equalsIgnoreCase(column.getColumnName(), "name")
					|| StringUtils.equalsIgnoreCase(column.getColumnName(), "title")
					|| StringUtils.equalsIgnoreCase(column.getColumnName(), "remarks")
					|| StringUtils.equalsIgnoreCase(column.getColumnName(), "update_date")) {
				column.setIsList("1");
			}

			// 查询字段
			if (StringUtils.equalsIgnoreCase(column.getColumnName(), "name")
					|| StringUtils.equalsIgnoreCase(column.getColumnName(), "title")) {
				column.setIsQuery("1");
			}

			// 查询字段类型
			if (StringUtils.equalsIgnoreCase(column.getColumnName(), "name")
					|| StringUtils.equalsIgnoreCase(column.getColumnName(), "title")) {
				column.setQueryType("like");
			}

			/* 设置特定类型和字段名 */
			if (StringUtils.startsWithIgnoreCase(column.getJdbcType(), "DATETIME")
					|| StringUtils.startsWithIgnoreCase(column.getJdbcType(), "DATE")
					|| StringUtils.startsWithIgnoreCase(column.getJdbcType(), "TIMESTAMP")) {
				column.setShowType("dateselect");
			}
			// 用户
			if (StringUtils.startsWithIgnoreCase(column.getColumnName(), "user_id")) {
				column.setJavaType(SysUser.class.getName());
				column.setJavaField(column.getJavaField().replaceAll("Id", ".id|name"));
				column.setShowType("userselect");
			}
			// 机构
			else if (StringUtils.startsWithIgnoreCase(column.getColumnName(), "org_id")) {
				column.setJavaType(SysOrg.class.getName());
				column.setJavaField(column.getJavaField().replaceAll("Id", ".id|name"));
				column.setShowType("orgselect");
			}
//			// 区域
//			else if (StringUtils.startsWithIgnoreCase(column.getColumnName(), "area_id")) {
//				column.setJavaType(SysArea.class.getName());
//				column.setJavaField(column.getJavaField().replaceAll("Id", ".id|name"));
//				column.setShowType("areaselect");
//			}
			// 创建者、更新者
			else if (StringUtils.startsWithIgnoreCase(column.getColumnName(), "create_by")
					|| StringUtils.startsWithIgnoreCase(column.getColumnName(), "update_by")) {
				column.setJavaType(SysUser.class.getName());
				column.setJavaField(column.getJavaField() + ".id");
			}
			// 创建时间、更新时间
			else if (StringUtils.startsWithIgnoreCase(column.getColumnName(), "create_date")
					|| StringUtils.startsWithIgnoreCase(column.getColumnName(), "update_date")) {
				column.setShowType("dateselect");
			}
			// 备注、内容
			else if (StringUtils.equalsIgnoreCase(column.getColumnName(), "remarks")
					|| StringUtils.equalsIgnoreCase(column.getColumnName(), "content")) {
				column.setShowType("textarea");
			}
			// 父级ID
			else if (StringUtils.equalsIgnoreCase(column.getColumnName(), "parent_id")) {
				column.setJavaType("This");
				column.setJavaField("parent.id|name");
				column.setShowType("treeselect");
			}
			// 所有父级ID
			else if (StringUtils.equalsIgnoreCase(column.getColumnName(), "parent_ids")) {
				column.setQueryType("like");
			}
			// 删除标记
			else if (StringUtils.equalsIgnoreCase(column.getColumnName(), "del_flag")) {
				column.setShowType("radiobox");
				column.setDictType("del_flag");
			}
		}
	}

	/**
	 * @desc 获取模板路径
	 * @return
	 */
	public static String getTemplatePath() {
		try {
			File file = new DefaultResourceLoader().getResource("").getFile();
			if (file != null) {
				return file.getAbsolutePath() + File.separator + StringUtils.replaceEach(GenUtils.class.getName(),
						new String[]{"util." + GenUtils.class.getSimpleName(), "."}, new String[]{"template", File.separator});
			}
		} catch (Exception e) {
			logger.error("{}", e);
		}
		return "";
	}

	/**
	 * @desc XML文件转换为对象
	 * @param fileName XML文件
	 * @param clazz 需要转换成的java类对象
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public static <T> T fileToObject(String fileName, Class<?> clazz) {
		try {
			String pathName = "/templates/modules/gen/" + fileName;
			logger.debug("File to object: {}", pathName);
			Resource resource = new ClassPathResource(pathName);
			InputStream is = resource.getInputStream();
			BufferedReader br = new BufferedReader(new InputStreamReader(is, "UTF-8"));
			StringBuilder sb = new StringBuilder();
			while (true) {
				String line = br.readLine();
				if (line == null) {
					break;
				}
				sb.append(line).append("\r\n");
			}
			if (is != null) {
				is.close();
			}
			if (br != null) {
				br.close();
			}
//			logger.debug("Read file content: {}", sb.toString());
			// 将xml字符串转换为java对象
			return (T) JaxbMapper.fromXml(sb.toString(), clazz);
		} catch (IOException e) {
			logger.warn("Error file convert: {}", e.getMessage());
		}
		return null;
	}

	/**
	 * @desc 获取代码生成配置对象
	 * @return 自动代码生成配置对象
	 */
	public static GenConfig getConfig() {
		return fileToObject("config.xml", GenConfig.class);
	}

	/**
	 * @desc 根据分类获取模板列表
	 * @param config 自动代码生成配置对象
	 * @param category 类别信息
	 * @param isChildTable 是否是子表
	 * @return 分类模板列表
	 */
	public static List<GenTemplate> getTemplateList(GenConfig config, String category, boolean isChildTable) {
		List<GenTemplate> templateList = Lists.newArrayList();
		if (config != null && config.getCategoryList() != null && category != null) {
			for (GenCategory e : config.getCategoryList()) {
				if (category.equals(e.getValue())) {
					List<String> list;
					if (!isChildTable) {
						list = e.getTemplate();
					} else {
						list = e.getChildTableTemplate();
					}
					if (list != null) {
						for (String s : list) {
							if (StringUtils.startsWith(s, GenCategory.CATEGORY_REF)) {
								templateList.addAll(getTemplateList(config, StringUtils.replace(s, GenCategory.CATEGORY_REF, ""), false));
							} else {
								GenTemplate template = fileToObject(s, GenTemplate.class);
								if (template != null) {
									templateList.add(template);
								}
							}
						}
					}
					break;
				}
			}
		}
		return templateList;
	}

	/**
	 * @desc 封装模板对象数据
	 * @param genScheme 数据模型
	 * @return
	 */
	public static Map<String, Object> getDataModel(GenScheme genScheme) {
		// 将生成方案对象转换成map对象
		Map<String, Object> model = BeanUtils.toMap(genScheme);

		// 设置生成文件时所需的其他参数信息
		model.put("lastPackageName", StringUtils.substringAfterLast((String) model.get("packageName"), "."));
		model.put("functionVersion", "1.0"); 														// 文件版本
		model.put("copyright", MyPropertyPlaceholderConfigurer.get("copyright")); 					// 文件版权
		model.put("datetime", DateUtils.format2DateStr(new Date(), DateUtils.DATE_PATTERN)); 		// 文件创建时间

		model.put("urlPrefix", model.get("moduleName") + (StringUtils.isNotBlank(genScheme.getSubModuleName())
				? "/" + StringUtils.lowerCase(genScheme.getSubModuleName()) : "") + "/" + model.get("className"));
		model.put("viewPrefix", //StringUtils.substringAfterLast(model.get("packageName"),".")+"/"+
				model.get("urlPrefix"));
		model.put("permissionPrefix", model.get("moduleName") + (StringUtils.isNotBlank(genScheme.getSubModuleName())
				? ":" + StringUtils.lowerCase(genScheme.getSubModuleName()) : "") + ":" + model.get("className"));

		model.put("dbType", PropertiesUtils.get("jdbc.type")); 											// 数据库类型

		return model;
	}

	/**
	 * @desc 生成到文件
	 * @param tpl 模板对象
	 * @param model 数据对象
	 * @param isReplaceFile 是否替换已存在的文件
	 * @return
	 */
	public static String generateToFile(GenTemplate tpl, Map<String, Object> model, boolean isReplaceFile) {
		// 获取生成文件
		String fileName = PropertiesUtils.getProjectPath() + File.separator
				+ StringUtils.replaceEach(FreeMarkerUtils.renderString(tpl.getFilePath() + "/", model),
				new String[]{"//", "/", "."}, new String[]{File.separator, File.separator, File.separator})
				+ FreeMarkerUtils.renderString(tpl.getFileName(), model);
		logger.debug(" fileName === " + fileName);

		// 获取生成文件内容
		String content = FreeMarkerUtils.renderString(StringUtils.trimToEmpty(tpl.getContent()), model);
		logger.debug(" content === \r\n" + content);

		// 如果选择替换文件，则删除原文件
		if (isReplaceFile) {
			FileUtils.deleteFile(fileName);
		}

		// 创建并写入文件
		if (FileUtils.createFile(fileName)) {
			FileUtils.writeToFile(fileName, content, true);
			logger.debug(" file create === " + fileName);
			return "生成成功：" + fileName + "<br/>";
		} else {
			logger.debug(" file extents === " + fileName);
			return "文件已存在：" + fileName + "<br/>";
		}
	}

	/**
	 * @desc 根据Jdbc类型获取对应的java类型
	 * @param jdbcType 数据库Jdbc类型
	 * @return
	 */
	private static String getJavaType(String jdbcType) {
		String javaType = null;
		Pattern pattern = Pattern.compile("([A-z]+)?+\\(\\d+\\)");
		Matcher matcher = pattern.matcher(jdbcType);
		if (matcher.matches()) {
			javaType = MyPropertyPlaceholderConfigurer.get(StringUtils.lowerCase(StringUtils.replaceBracket(jdbcType, "0", null )));
		}

		if(StringUtils.isNull(javaType)) {
			javaType = MyPropertyPlaceholderConfigurer.get(StringUtils.lowerCase(StringUtils.replaceBracket(jdbcType)), "unknowType");
		}
//		String javaType = MyPropertyPlaceholderConfigurer.get(StringUtils.lowerCase(StringUtils.replaceBracket(column.getJdbcType())), "unknowType");
		return javaType;
	}

	/**
	 * @desc 判断指定的java数据类型是否在 基本的与数据库对应的java类型列表中;如果存在则返回true,否则返回false
	 * @param baseJavaTypes 基本的与数据库对应的java类型列表
	 * @param javaType 需要判断的java数据类型
	 * @return boolean true 表示不是基本的Java类型对象
	 */
	public static boolean checkBaseJavaType(List<SysDict> baseJavaTypes, String javaType) {

		// 先将list集合转换成Map集合(jdk1.8的luma表达式)
//		Map<String, SysDict> sysDictMap = baseJavaTypes.stream().collect(
//				Collectors.toMap(SysDict::getValue, (p) -> p));
		Map<String, SysDict> sysDictMap = new HashMap<>(1);
		for(SysDict sysDict : baseJavaTypes) {
			sysDictMap.put(sysDict.getValue(), sysDict);
		}
		if(BeanUtils.isNull(sysDictMap.get(javaType))) return true;
		return false;
	}

	public static void main(String[] args) {
		try {
//			GenConfig config = getConfig();
//			System.out.println(config);
//			System.out.println(JaxbMapper.toXml(config));
//			System.out.println("======================================");
//			String str = StringUtils.lowerCase(null);
//			System.out.println(str);
//			不知道你的匹配环境是怎么样的
//			用这个吧：\w+? \d{1,2},\d{4}，问号后面有空格
//			如果不要逗号，可以这样：(\w+? \d{1,2}),(\d{4})，取匹配子文本1和2，就可以了
//			你的 [a-z]+\s [0-9]{2},[0-9]{4}，前面的 [a-z]+，没有包括大写和数字；[0-9]{2}，匹配两次，一个数字的不匹配；式中的逗号是在匹配文本里面的，只是不知道你是否需要。
			// number=java.math.BigDecimal
//
//			String str4 = "decimal(18)";
////			str.replaceAll("\\(.*?\\)|\\{.*?}|\\[.*?]|(.*?)", "");
//			String javaType = StringUtils.lowerCase(StringUtils.replaceBracket(str4));
//			System.out.println(javaType);
//
////			Pattern pattern = Pattern.compile("([A-z]+)?+\\(\\d+,\\d+\\)");
//			Pattern pattern = Pattern.compile("([A-z]+)?+\\(\\d+\\)");
//			Matcher matcher = pattern.matcher(str4);
//			if (matcher.matches()) {
//				System.out.println("111111111111");
//			} else {
//				System.out.println("fdasfdasfd");
//			}

			String jdbcType = "varchar(18)";
			Map<String, String> dataMap = new HashMap<>();
			dataMap.put("float", "java.lang.Float");
			dataMap.put("double", "java.lang.Double");
			dataMap.put("decimal", "java.math.BigDecimal");
			dataMap.put("number", "java.math.BigDecimal");
			dataMap.put("decimal(0)", "java.lang.Long");
			dataMap.put("number(0)", "java.lang.Long");
			dataMap.put("varchar", "java.lang.String");

			String value = null;
			Pattern pattern = Pattern.compile("([A-z]+)?+\\(\\d+\\)");
			Matcher matcher = pattern.matcher(jdbcType);
			if (matcher.matches()) {
				value = dataMap.get(StringUtils.lowerCase(StringUtils.replaceBracket(jdbcType, "0", null )));
			}

			if(StringUtils.isNull(value)) {
				value = dataMap.get(StringUtils.lowerCase(StringUtils.replaceBracket(jdbcType)));
			}
			System.out.println(value);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}